/*
 * RPC.java
 *
 */

package net.kernub.client.core;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;

import com.google.gwt.http.client.URL;

import java.lang.Object;
import java.lang.Iterable;
import java.util.ArrayList;
import java.util.Iterator;

import net.kernub.client.core.RPCCallback;
import net.kernub.client.core.RPCLoadingHandler;

/*
RPC.call( svc, mth, arg0, arg1, callback );
*/

// TODO: add a way to execute call in a DeferredCommand instead of directly

/*
 * RPC Class
 */
public class RPC implements RequestCallback
{
	/*
	 * Members
	 */
	private static final String url = "backend/rpc.php";

	private static RPCLoadingHandler loadingHandler = null;

	private static int loadingStarted = 0;

	private String serviceName = null;
	private String methodName = null;
	private RPCCallback callback = null;

	// constructor
	private RPC( String serviceName, String methodName, RPCCallback callback )
	{
		this.serviceName = serviceName;
		this.methodName = methodName;
		this.callback = callback;
	}


	public static void setLoadingHandler( RPCLoadingHandler handler )
	{
		RPC.loadingHandler = handler;
	}

	public static void call( String svc, String mth, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		rpc.remoteCall( null );
	}

	public static void call( String svc, String mth, Object a0, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0);
		rpc.remoteCall( args );
	}

	public static void call( String svc, String mth, Object a0, Object a1, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0) + "&" + rpc.encodeArg("a1", a1);
		rpc.remoteCall( args );
	}

	public static void call( String svc, String mth, Object a0, Object a1, Object a2, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0) + "&" + rpc.encodeArg("a1", a1) + "&" + rpc.encodeArg("a2", a2);
		rpc.remoteCall( args );
	}

	public static void call( String svc, String mth, Object a0, Object a1, Object a2, Object a3, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0) + "&" + rpc.encodeArg("a1", a1) + "&" + rpc.encodeArg("a2", a2) + "&" +
									rpc.encodeArg("a3", a3);
		rpc.remoteCall( args );
	}

	public static void call( String svc, String mth, Object a0, Object a1, Object a2, Object a3, Object a4, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0) + "&" + rpc.encodeArg("a1", a1) + "&" + rpc.encodeArg("a2", a2) + "&" +
									rpc.encodeArg("a3", a3) + "&" + rpc.encodeArg("a4", a4);
		rpc.remoteCall( args );
	}

	public static void call( String svc, String mth, Object a0, Object a1, Object a2, Object a3, Object a4, Object a5, RPCCallback cb )
	{
		RPC rpc = new RPC( svc, mth, cb );
		String args = rpc.encodeArg("a0", a0) + "&" + rpc.encodeArg("a1", a1) + "&" + rpc.encodeArg("a2", a2) + "&" +
									rpc.encodeArg("a3", a3) + "&" + rpc.encodeArg("a4", a4) + "&" + rpc.encodeArg("a5", a5);
		rpc.remoteCall( args );
	}

	// detect argument type and encode it
	public String encodeArg( String argName, Object a )
	{	
		// TODO: can identify if the class implement 'Iterable' interface, if so, process! :)
		/*
		Class<Object> listIf = a.getClass().getInterfaces();
		for( Class c : listIf )
		{
			if( c == Iterable.class )
				GWT.log( "encodeArg: argName: " + argName + " is iterable!!!!! YOUPI!", null );
		}
		*/

		String r = "";

		if( a.getClass() == ArrayList.class )
		{
			final ArrayList<Object> arr = (ArrayList<Object>) a;

			// empty, create argument anyway -> no!!! this is creating an pseudo-empty-array on server side!
			if( arr.size() == 0 )
			{
				//r = argName + "[]=";
				return r;
			}

			int count = arr.size();
			for( Iterator<Object> it = arr.iterator(); it.hasNext(); )
			{
				r += argName + "[]=" + URL.encode( it.next().toString() );
				count--;
				if( count > 0 )
					r += "&";
			}

			return r;
		}

		r = argName + "=" + URL.encode( a.toString() );
		return r;
	}

	private void remoteCall( String args )
	{
		// TODO: find a way to send arguments via POST

		// be carefull when no arguments...
		if( args != null )
			args = "&" + args;
		else
			args = "";
		// TODO: change method call to use the path instead of serviceName and serviceMethod
		//String url = GWT.getHostPageBaseURL() + this.url + "?svc=" + URL.encode(this.serviceName) + "&mth=" + URL.encode(this.methodName) + args;
		String url = GWT.getHostPageBaseURL() + this.url + "?p=/" + URL.encode(this.serviceName) + "/" + URL.encode(this.methodName) + args;

		//GWT.log( "remoteCall: url: " + url, null );
		//GWT.log( "remoteCall: args: " + args, null );

		RequestBuilder builder = new RequestBuilder( RequestBuilder.GET, url );
		try
		{
			// TODO: find a way to send arguments via POST
			Request request = builder.sendRequest( null, this );
			startLoading();
		}
		catch( RequestException e )
		{
			// couldn't retreive JSON
			this.callback.onError( e.getMessage() );
		}
	}

	// implement RequestCallback
	public void onError( Request request, Throwable exception )
	{
		stopLoading();
		// couldn't retrieve JSON
		this.callback.onError( exception.getMessage() );
	}

	// implement RequestCallback
	public void onResponseReceived( Request request, Response response )
	{
		stopLoading();
		if( response.getStatusCode() != 200 )
		{
			// couldn't retreive JSON + response.getStatusText()
			this.callback.onError( response.getStatusText() );
		}
		else
		{
			//Window.alert( response.getText() );
			this.callback.callFromJSON( response.getText() );
		}
	}

	private void startLoading()
	{
		if( RPC.loadingStarted == 0 && RPC.loadingHandler != null )
		{
			RPC.loadingHandler.onLoadingStart();
		}
		RPC.loadingStarted++;
	}

	private void stopLoading()
	{
		RPC.loadingStarted--;
		if( RPC.loadingStarted == 0 && RPC.loadingHandler != null )
		{
			RPC.loadingHandler.onLoadingStop();
		}
	}
}

